/**
* dho
* decimal, hexadecimal and octal number display utility,
* also capable of displaying/converting binary format
* author: Xia Zuoling
* modified: 14:08:41 2007.04.05
* Wed Apr 11 17:09:04 CST 2007
* * default binary on
* version: 0.24
* TODO: negative numbers
*/

#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

#define ERRMSG_MAX (255)
char errmsg[ERRMSG_MAX];

int usage(void);
int error(const char* msg, int ret);
int warning(const char* msg, int ret);
int parse_number(const char *arg, long *num);
int dho(long num, int binary);

int main(int argc, char *argv[])
{
    int i, binary;
    binary = 1;
    if (argc < 2)
        return usage();
    for (i = 1; i < argc; i++)
    {
        long n;
        if (argv[i][0] == '/')
        {
            switch (argv[i][1])
            {
            case 0:
                sprintf(errmsg, "%s: invalid option.", argv[i]);
                warning(errmsg, 0);
                continue;

            case 'b':
                binary = 1;
                continue;

            case 'B':
                binary = 0;
                continue;

            default:
                sprintf(errmsg, "%s: unknown option.", argv[i]);
                warning(errmsg, 0);
                continue;
            }
        }
        if (parse_number(argv[i], &n))
            continue;
        fprintf(stdout, "%s: ", argv[i]);
        dho(n, binary);
    }
    return 0;
}

int usage(void)
{
    fprintf(stderr, "       dho - The Decimal, Hexadecimal, Octal Display Utility.\n"
                    "usage: dho /[bB] [ NUM ... ]\n"
                    "\n"
                    "`NUM' is interpreted via its prefix:\n"
                    "   0b: Binary integer.\n"
                    "    0: Octal integer.\n"
                    "  1-9: Decimal integer.\n"
                    "   0x: Hexadecimal integer.\n"
                    "\n"
                    "The output is in the following format:\n"
                    "  <Input>: <Decimal>, <Hexadecimal>, <Octal>; <Binary>\n"
                    "Where the trailing '; <Binary>' part is controlled with\n"
                    "the switch `/[bB]':\n"
                    "\n"
                    "/b     switches 'binary' mode on.\n"
                    "/B     switches 'binary' mode off. (default)\n"
                    "\n"
                    "`DHO' utility version 0.24, build 20070405\n"
                    "copyleft zerox (c) 2006, all rights reserved.\n"
                    "E-mail: zeroxia@gmail.com\n"
    );
    return 1;
}

/**
* print decimal hexadecimal octal, optional binary.
*/
int dho(long num, int binary)
{
    fprintf(stdout, "%ld, 0x%lX, 0%lo", num, (unsigned long)num, (unsigned long)num);
    if (binary)
    {
        int len = sizeof(long) * 8; /* no. of bits */
        int i;
        fprintf(stdout, "; ");
        for (i = len-1; i >= 0; i--)
        {
            putchar((num & (1 << i)) ? '1' : '0');
            if (i % 8 == 0)
                fprintf(stdout, " ");
            /* commented: 2007.04.05
            else if (i % 4 == 0)
                fprintf(stdout, ",");
                */
        }
    }
    fprintf(stdout, "\n");
    return 0;
}

int parse_number(const char* arg, long *num)
{
    long n;
    char* ptr;
    const char *original_arg;

    /** pre-processing **/
    original_arg = arg;
    while (*arg && isspace(*arg)) arg++; /* skip leading spaces */
    if (*arg == 0) /* empty string */
    {
        sprintf(errmsg, "%s: empty string.", original_arg);
        return warning(errmsg, 1);
    }
    /** binary? **/
    if (arg[0] == '0' && tolower(arg[1]) == 'b') /* binary input */
    {
        int i;
        if (arg[2] != '0' && arg[2] != '1') /* invalid */
        {
            sprintf(errmsg, "%s: invalid binary input.", arg);
            return warning(errmsg, 1);
        }
        n = 0; /* start summing */
        for (i = 2; arg[i] && (arg[i] == '0' || arg[i] == '1'); i++) /* traverse through each char */
        {
            n = n * 2 + (arg[i] - '0');
        }
    }
    else
    {
        /** otherwise, let `strtol' do the parsing **/
        n = strtol(arg, &ptr, 0);
        /* printf("\terrno = %d\n", errno); */
        if (ptr == arg)
        {
            sprintf(errmsg, "%s: invalid number.", arg);
            return warning(errmsg, 1);
        }
        if (errno == ERANGE)
        {
            sprintf(errmsg, "%s: sorry: out of range.", arg);
            return warning(errmsg, 1);
        }
    }
    *num = n;
    return 0;
}

int warning(const char* msg, int ret)
{
    if (msg)
        fprintf(stderr, "warning: %s\n", msg);
    else
        fprintf(stderr, "warning: %s\n", errmsg);

    return ret;
}

/*
int error(const char* msg, int ret)
{
    if (msg)
        fprintf(stderr, "%s\n", msg);
    else
        fprintf(stderr, "%s\n", errmsg);
    if (ret)
        exit(ret);
    return ret;
}
*/
